﻿#include "pch.h"
#include "device_scan.h"




#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 26451)
#pragma warning(disable : 6386)

#define LOG_TAG		"设备扫描"
#define LOG_LVL		LOG_LVL_DBG
#include "spk_ulog.h"


void ip_addr_trans(string ip_str, u8 ip[4])
{
	struct in_addr var_ip;
	inet_pton(AF_INET, ip_str.c_str(), (void*)&var_ip);
	ip[0] = var_ip.s_addr & 0xFF;
	ip[1] = (var_ip.s_addr >> 8) & 0xFF;
	ip[2] = (var_ip.s_addr >> 16) & 0xFF;
	ip[3] = (var_ip.s_addr >> 24) & 0xFF;
}

string ip_addr_trans(u8 ip[4])
{
	string ip_str = std::to_string(ip[0]) + "." + std::to_string(ip[1]) + "." + std::to_string(ip[2]) + "." + std::to_string(ip[3]);
	return ip_str;
}

int device_scan::udp_init(int port)
{
	int sock = (int)::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	if (sock < 0)
	{
		LOG_E("socket创建失败!");
		return -1;
	}

	if (port > 0)
	{
		REUSE_PORT(sock)
		struct sockaddr_in addr;
		memset(&addr, 0, sizeof(addr));

		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = INADDR_ANY;
		addr.sin_port = htons(port);

		int err = ::bind(sock, (struct sockaddr*)&addr, sizeof(addr));

		if (err < 0)
		{
			LOG_E("socket绑定失败!");
			closesocket(sock);
			return -1;
		}
	}
	return sock;
}


device_scan::device_scan(string _name, string _my_ip_addr)
{
	count = 0;
	fd = udp_init(DEVICE_SCAN_BROADCAST_PORT);
	rx_thread = nullptr;
	notify_win = nullptr;
	ctrl = nullptr;
	my_ip_addr = _my_ip_addr;
	name = _name;

	if (fd > 0)
	{
		rx_thread = new std::thread(device_scan::rx_packet_thread, this);
		//rx_packet_thread(this);
	}
}


void device_scan::on_rx_packet(void* rx_buffer, int len, u8 type, void* param)
{
	device_scan* ins = (device_scan*)param;

	switch (type)
	{
		/*收到设备发来的信息*/
	case CMD_DEVICE_INFO:
#ifdef _WIN32
		if (ip_addr_trans((u8*)rx_buffer) != ins->my_ip_addr)
		{
			::SendMessage(ins->notify_win, MSG_DEVICE_INFO, (WPARAM)&ins->count, (LPARAM)(rx_buffer));
			ins->count++;
		}
#endif
		break;
		/*收到来自于上位机的扫描请求信息*/
	case CMD_DEVICE_SCAN:
	{
		u8    my_ip[4];
		u8    pc_ip[4];
		int   len = 0;
		char* buffer = nullptr;
		memcpy(pc_ip, rx_buffer, 4);

		LOG_D("recv scan msg from(%u.%u.%u.%u)", pc_ip[0], pc_ip[1], pc_ip[2], pc_ip[3]);

		ip_addr_trans(ins->my_ip_addr, my_ip);
		len = ins->name.length() + 1 + 4;
		buffer = new char[len];
		memset(buffer, 0, len);
		memcpy(buffer, my_ip, 4);									/*前4个字节为IP地址*/
		memcpy(buffer + 4, ins->name.c_str(), ins->name.length());	/*后面跟着设备名称*/
		
		string ip = ip_addr_trans(pc_ip);
		int ret = ins->send_packet(CMD_DEVICE_INFO, buffer, len, &ip);

		if (ret == len+6)
		{
			if (ins->ctrl == nullptr)
			{
				/*检测到PC，创建TCP服务器接收PC端控制指令*/
				ins->ctrl = new device_ctrl;
				ins->ctrl->run();
			}
		}
		delete[]buffer;
	}
	break;
	}
}

void device_scan::rx_packet_thread(void* arg)
{
	device_scan* ins = (device_scan*)arg;
	spk_packet::recv_packet_thread_ex(ins->io_read, ins->on_rx_packet, ins);
}

device_scan::~device_scan()
{
	if (fd > 0) closesocket(fd);
	fd = -1;
	count = 0;
	if (ctrl)
	{
		delete ctrl;
		ctrl = nullptr;
	}
}

int device_scan::scan(void)
{
	struct in_addr var_ip;

	/*发送内容为PC端IP地址*/
	inet_pton(AF_INET, my_ip_addr.c_str(), (void*)&var_ip);

	/*发送目标为广播地址*/
	// 如果 my_ip_addr 是 "192.168.1.1"，经过这段代码处理后，ip 将变为 "192.168.1.255"，这就是该子网的广播地址
	size_t pos = my_ip_addr.find_last_of('.');
	string ip  = my_ip_addr.substr(0, pos);
	ip         = ip + std::string(".255");

	return send_packet(CMD_DEVICE_SCAN, &var_ip, 4,&ip);
}

int device_scan::io_write(void* data, int len, void* param)
{
	if (fd < 0) return fd;

	count = 0;

	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	
	string ip            = *(string*)param;
	addr.sin_family      = AF_INET;
	addr.sin_addr.s_addr = inet_addr(ip.c_str());
	addr.sin_port        = htons(DEVICE_SCAN_BROADCAST_PORT);

	int ret = sendto(fd, (char*)data, len, 0, (struct sockaddr*)&addr, sizeof(addr));

	if (ret < 0)
	{
		LOG_E("socket发送失败!");
		return -1;
	}

	return ret;
}

int device_scan::io_read(void* data, int len, int time_out_ms, void* param)
{
	struct sockaddr_in addr;
	socklen_t addr_len = sizeof(addr);
	int sock = ((device_scan*)param)->fd;
	int _len = recvfrom(sock, (char*)data, len, 0, (struct sockaddr*)&addr, &addr_len);
	return _len;
}


#pragma warning(pop)
